﻿// /*************************************************************************
//  *
//  *  Copyright (C) 2010 - 2011 Sonar Team
//  *
//  *  This program is free software: you can redistribute it and/or modify
//  *  it under the terms of the GNU General Public License as published by
//  *  the Free Software Foundation, either version 3 of the License, or
//  *  (at your option) any later version.
//  *
//  *  This program is distributed in the hope that it will be useful,
//  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  *  GNU General Public License for more details.
//  *
//  *  You should have received a copy of the GNU General Public License
//  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
//  *
//  *************************************************************************/

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;

using MySql.Data.MySqlClient;

namespace NightWolf.Sonar
{
    public class SonarRecord
    {
        /* ======================
         * Public methods
         * ====================== */

        #region Public Methods

        /// <summary>
        /// Update this object with the primary key for criterion
        /// </summary>
        public void Update()
        {
            switch (SonarProvider.DbType)
            {
                case SonardDatabase.MYSQL:
                    var mysql_query = new StringBuilder("UPDATE " + tableName + " SET ");
                    var values = new List<string>();
                    foreach (var f in fields)
                    {
                        values.Add(f.Field + "='" + this.GetType().GetField(f.Field).GetValue(this) + "'");
                    }
                    mysql_query.Append(string.Join(",", values));
                    mysql_query.Append(" WHERE " + getPrimary().Field + "='" + this.GetType().GetField(getPrimary().Field).GetValue(this) + "'");
                    var cmd = new MySqlCommand(mysql_query.ToString(), SonarProvider.MYSQL_CONNECTION).ExecuteNonQuery();
                    break;
            }
        }

        /// <summary>
        /// Insert this object in the database
        /// </summary>
        public void Insert()
        {
            switch (SonarProvider.DbType)
            {
                case SonardDatabase.MYSQL:
                    var mysql_query = new StringBuilder("INSERT INTO " + tableName + " (" + string.Join(",", this.fields) + ") VALUES ");
                    var values = new List<string>();
                    foreach (var f in fields)
                    {
                        var v = this.GetType().GetField(f.Field).GetValue(this);
                        values.Add("'" + v + "'");
                    }
                    mysql_query.Append("(" + string.Join(",", values) + ")");
                    var cmd = new MySqlCommand(mysql_query.ToString(), SonarProvider.MYSQL_CONNECTION).ExecuteNonQuery();
                    break;
            }
        }

        /// <summary>
        /// Delete this object with the primary key for criterion
        /// </summary>
        public void Delete()
        {
            switch (SonarProvider.DbType)
            {
                case SonardDatabase.MYSQL:
                    var mysql_query = new StringBuilder("DELETE FROM " + tableName + " WHERE " + getPrimary().Field + " = '" + 
                        this.GetType().GetField(getPrimary().Field).GetValue(this) + "'");
                    var cmd = new MySqlCommand(mysql_query.ToString(), SonarProvider.MYSQL_CONNECTION).ExecuteNonQuery();
                    break;
            }

        }

        /// <summary>
        /// Select the first result in the list with the criterion
        /// </summary>
        /// <typeparam name="T">Type of your object</typeparam>
        /// <param name="criterion">Yor criterion exemple : test='test'</param>
        /// <returns>Your object or null</returns>
        public static T FindFirst<T>(string criterion)
        {
            var objs = FindAll<T>(criterion);
            if (objs.Count > 0)
            {
                return objs[0];
            }
            else
            {
                return default(T);
            }
        }

        /// <summary>
        /// Select the all result in the list with the criterion
        /// </summary>
        /// <typeparam name="T">Type of yours object</typeparam>
        /// <param name="criterion">Yor criterion exemple : test='test'</param>
        /// <returns>Yours object or null</returns>
        public static List<T> Find<T>(string criterion)
        {
            return FindAll<T>(criterion);
        }

        /// <summary>
        /// Select all results for this table, dont use criterion parameters with this method, please use Find(T)
        /// </summary>
        /// <typeparam name="T">Type of yours object</typeparam>
        /// <param name="criterion">Don't use please</param>
        /// <returns>Yours object</returns>
        public static List<T> FindAll<T>(string criterion = "")
        {
            var objs = new List<T>();
            var fields = getFields(typeof(T));
            var query = new StringBuilder("SELECT * FROM " + getTableName(typeof(T)));
            if (criterion != "")
            {
                query.Append(" WHERE " + criterion);
            }

            switch (SonarProvider.DbType)
            {
                case SonardDatabase.MYSQL:
                    var mysql_cmd = new MySqlCommand(query.ToString(), SonarProvider.MYSQL_CONNECTION);
                    var mysql_reader = mysql_cmd.ExecuteReader();
                    while (mysql_reader.Read())
                    {
                        T obj = (T)Activator.CreateInstance(typeof(T));
                        if (obj != null)
                        {
                            foreach (var field in fields)
                            {
                                switch (field.Infos)
                                {
                                    case SonarFieldType.INT:
                                        obj.GetType().GetField(field.Field).SetValue(obj, (int)mysql_reader[field.Field]);
                                        break;

                                    case SonarFieldType.STRING:
                                        obj.GetType().GetField(field.Field).SetValue(obj, (string)mysql_reader[field.Field]);
                                        break;
                                }
                            }
                        }
                        objs.Add(obj);
                    }
                    mysql_reader.Close();
                    break;
            }
            return objs;
        }

        #endregion

        /* ======================
         * Private methods
         * ====================== */

        #region Private Methods

        private static string getTableName(Type type)
        {
            return ((SonarTable)type.GetCustomAttributes(typeof(SonarTable), true)[0]).Table;
        }

        private static List<SonarField> getFields(Type type)
        {
            var fields = new List<SonarField>();
            foreach (var fc in type.GetFields())
            {
                if (fc.GetCustomAttributes(typeof(SonarField), true).Length > 0)
                {
                    var attr = (SonarField)fc.GetCustomAttributes(typeof(SonarField), true)[0];
                    attr.Infos = getTypeOfField(fc);
                    fields.Add(attr);
                }
            }
            return fields;
        }

        private string tableName
        {
            get
            {
                return ((SonarTable)this.GetType().GetCustomAttributes(typeof(SonarTable), true)[0]).Table;
            }
        }

        private List<SonarField> fields
        {
            get
            {
                var fields = new List<SonarField>();
                foreach (var fc in this.GetType().GetFields())
                {
                    if (fc.GetCustomAttributes(typeof(SonarField), true).Length > 0)
                    {
                        var attr = (SonarField)fc.GetCustomAttributes(typeof(SonarField), true)[0];
                        attr.Infos = getTypeOfField(fc);
                        fields.Add(attr);
                    }
                }
                return fields;
            }
        }

        private static SonarFieldType getTypeOfField(FieldInfo f)
        {
            switch (f.FieldType.Name.ToLower())
            {
                case "int32":
                    return SonarFieldType.INT;

                case "string":
                    return SonarFieldType.STRING;

                default:
                    return SonarFieldType.INT;
            }
        }

        private SonarField getPrimary()
        {
            var p = this.fields.FirstOrDefault(x => x.IsPrimary);
            return p;
        }

        #endregion
    }

    #region SonarFieldType

    public enum SonarFieldType
    {
        INT = 1,
        STRING = 2,
        LONG = 3,
    }

    #endregion

    #region Attributes

    public class SonarTable : Attribute
    {
        public string Table { get; set; }

        public SonarTable(string table)
        {
            this.Table = table;
        }
    }

    public class SonarField : Attribute
    {
        public string Field { get; set; }
        public SonarFieldType Infos = SonarFieldType.STRING;
        public bool IsPrimary { get; set; }

        public SonarField(string field, bool isPrimary = false)
        {
            this.Field = field;
            this.IsPrimary = isPrimary;
        }

        public override string ToString()
        {
            return this.Field;
        }
    }

    #endregion
}
